﻿#pragma once

#include "common.h"
#include <vector>
#include <set>

namespace anip
{
	class RenderBuffer;
	
	enum class KnownCursor
	{
		None = 0x36fff,
		Arrow = 0x36001, // GLFW_ARROW_CURSOR
		IBeam = 0x36002, // GLFW_IBEAM_CURSOR
		Crosshair = 0x36003, // GLFW_CROSSHAIR_CURSOR
		Hand = 0x36004, // GLFW_HAND_CURSOR
		HResize = 0x36005, //GLFW_HRESIZE_CURSOR
		VResize = 0x36006, //GLFW_VRESIZE_CURSOR
	};

	class MonitorInfo
	{
		friend class Context;

	private:
		void* _handle;
		MonitorInfo(void* hnd);

	public:
		vec2 position() const;
		vec2 resolution() const;
		vec2 physicalSize() const; // in millimeters
		vec2 contentScale() const;
		const wstring name() const;
	};

	struct UserEvent
	{
#pragma warning(suppress: 26495)
		UserEvent() {}

		enum class Type
		{
			None, 
			Key,			// provides the `key` struct
			UnicodeInput,	// provides `wchar_t unicode`
			MouseMove,		// provides `vec2 pos`
			MouseAction,	// provides the `mouse` struct
			Scroll,			// provides `vec2 scroll`
			Drop,			// provides the `drop` struct
			Resize,			// provides no parameter
			Reposition		// provides no parameter
		} type = Type::None;

		union
		{
			struct { Key value; ButtonAction action; } key;
			struct { MouseButton button; ButtonAction action; } mouse;
			struct { const char** paths; int count; } drop;
			wchar_t unicode;
			vec2 scroll;
			vec2 pos;
		};
	};

	// NOTE: most positions and sizes used in Context functions are to be interpreted
	// as in PIXELS instead of SCREEN COORDINATES, even if they refer to windows.
	class Context
	{
		friend void finalize();
		friend class RenderBuffer;

	private:
		struct Impl;
		static void finalize();

		static void _pushBuffer(RenderBuffer& buf);
		static RenderBuffer* _popBuffer();

	public:
		Context() = delete;

		// defaults to 80
		static double maxFramerate;

		static void init(vec2 size, const MonitorInfo* mon = nullptr);
		static RenderBuffer& defaultTarget();
		static RenderBuffer& currentTarget();
		static MonitorInfo getPrimaryMonitor();
		static MonitorInfo findCurrentMonitor();
		static const std::vector<MonitorInfo> getMonitors();

		static vec2 size();
		static vec2 position();
		static vec2 contentScale();
		static wstring title();

		static void setSize(invec2 size);
		static void setPosition(invec2 pos);
		static void setTitle(const wstring& title);
		static void setCursor(KnownCursor c);

		static bool pollEvent(UserEvent& ev);

		static vec2 mousePos();
		static void setMousePos(invec2 pos);
		static const std::set<Key> getHoldingKeys();
		static bool holdingShift();
		static bool holdingControl();
		static bool holdingAlt();
		static bool holdingMouseButton(MouseButton btn);

		static bool windowShouldClose();
		static void markWindowShouldClose();

		static void clear();
		static void clear(color c);
		static void fill();

		static void show();
		static void showFullscreen(MonitorInfo* mon);

		static void startFrame();
		static void endFrame();
		static double deltaTime();
		static double dampedDeltaTime();

		static void setScissor(invec2 p, invec2 s, bool absolute = false);
		static void addScissor(invec2 p, invec2 s, bool absolute = false);
		static void getScissor(outvec2 p, outvec2 s, bool absolute = false);
		static void noScissor();

		static void update();

		static void doEvents(bool wait = false);
	};
}